﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using GPSMap.Core.Properties;
using MapControl.Misc;
using MapControl.Misc.DAL;

namespace MapControl
{
    public abstract class BaseGPSPoint
    {
        //public string Name;
        //public DateTime LocalTime;
        //public double LonGPS;
        //public double LatGPS;
        //public double Alt;
        //public double Speed;
        //public double Angle;
        //public double Distance;

        public GPSData objGPSData;
        public PointF CenterPoint;
        
        protected float r = 0.02f;

        //Hình chữ nhật 1
        private RectangleF _rect;
        /// <summary>
        /// Get number of handles
        /// </summary>
        public RectangleF Rect
        {
            get { return _rect; }
            set { _rect = value; }
        }

        private bool selected;
        /// <summary>
        /// Selection flag
        /// </summary>
        public bool Selected
        {
            get
            {
                return selected;
            }
            set
            {
                selected = value;
            }
        }

        /// <summary>
        /// Get number of handles
        /// </summary>
        public int HandleCount
        {
            get
            {
                return 8;
            }
        }

        
        public BaseGPSPoint()
        {
            InitRect();
        }

        public BaseGPSPoint(string name, double lon, double lat, double alt, double speed, double angle, DateTime time, int satnum)
        {
            objGPSData = new GPSData(name, lon, lat, alt, speed, angle, time, satnum);
            InitRect();
        }

        public BaseGPSPoint(DataRow row)
        {
            objGPSData = new GPSData(row);
            InitRect();
        }

        ///// <summary>
        ///// Tính khoảng cách giữa 2 điểm theo km
        ///// </summary>
        ///// <param name="pointTo"></param>
        ///// <returns></returns>
        //public double CalculateDistance(BaseGPSPoint pointTo)
        //{
        //    double distance = AxMap1.Distance(objGPSData.LonGPS, objGPSData.LatGPS, pointTo.objGPSData.LonGPS, pointTo.objGPSData.LatGPS) / 1000;
        //    return distance;
        //}

        //public PointF GetPoint()
        //{
        //    float x, y;
        //    x = y = 0;
        //    AxMap1.ConvertCoord(ref x, ref y, ref LonGPS, ref LatGPS, MapXLib.ConversionConstants.miMapToScreen);
        //    return new PointF(x, y);
        //}

        #region Drawing

        /// <summary>
        /// Vẽ tọa độ gps bằng file ảnh
        /// </summary>
        /// <param name="g"></param>
        /// <param name="curBitmap"></param>
        private void DrawImage(Graphics g, Bitmap curBitmap)
        {
            InitRect();
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            curBitmap = RotateImage(curBitmap, (float)objGPSData.Angle);
            RectangleF compressionRectangle = new RectangleF(CenterPoint.X - (float)curBitmap.Width / 4, CenterPoint.Y - (float)curBitmap.Height / 4, (float)curBitmap.Width / 2, (float)curBitmap.Height / 2);
            g.DrawImage(curBitmap, compressionRectangle);
            //g.DrawRectangle(new Pen(Color.Orange), _rect.X, _rect.Y, _rect.Width, _rect.Height);
            //g.DrawImage(curBitmap, _rect);
            
            // Dispose of objects
            curBitmap.Dispose();
        }

        /// <summary>
        /// Rotate image by custom angle
        /// </summary>
        /// <param name="bmp"></param>
        /// <param name="angle"></param>
        /// <returns></returns>
        private Bitmap RotateImage(Bitmap bmp, float angle)
        {
            Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
            using (Graphics g = Graphics.FromImage(rotatedImage))
            {
                g.TranslateTransform(bmp.Width / 2.0f, bmp.Height / 2.0f); //set the rotation point as the center into the matrix
                g.RotateTransform(angle); //rotate
                g.TranslateTransform(-bmp.Width / 2.0f, -bmp.Height / 2.0f); //restore rotation point into the matrix
                g.DrawImage(bmp, new Point(0, 0)); //draw the image on the new bitmap
            }

            return rotatedImage;
        }

        /// <summary>
        /// Vẽ lộ trình
        /// </summary>
        /// <param name="g"></param>
        /// <param name="pointTo"></param>
        public void DrawRoute(Graphics g, Color col, BaseGPSPoint pointTo)
        {
            //Nếu distance nhỏ hơn 50 km thì mới vẽ
            //if ((CalculateDistance(pointTo)) < 50)
            //{
                Pen pen = new Pen(col, 2);
                g.DrawLine(pen, this.CenterPoint, pointTo.CenterPoint);
                pen.Dispose();
            //}
        }

        #region Custom Draw
        
        protected void DrawCircleImage(Graphics g)
        {
            InitRect();
            g.DrawImage(Resources.circle, Rect);
        }

        protected void DrawCircle(Graphics g, Color color)
        {
            Pen pen = new Pen(color, 2);

            InitRect();

            SolidBrush brush = new SolidBrush(color);
            g.FillEllipse(brush, Rect);

            pen.Dispose();
        }

        protected void DrawCircle(Graphics g, Color colorFill, Color colorBorder)
        {
            Pen pen = new Pen(colorBorder, 2);

            InitRect();

            SolidBrush brush = new SolidBrush(colorFill);
            g.FillEllipse(brush, Rect);
            g.DrawEllipse(pen, Rect);

            pen.Dispose();
        }

        /// <summary>
        /// Vẽ ký hiệu mũi tên máy bay
        /// </summary>
        /// <param name="g"></param>
        /// <param name="color"></param>
        private void DrawSymbol(Graphics g, Color color)
        {
            Pen pen = new Pen(color, 1);
            SolidBrush brush = new SolidBrush(color);
            InitRect();

            PointF[] pArray = new PointF[6];
            //Line1
            pArray[0] = new PointF(Rect.X, Rect.Y);
            pArray[1] = new PointF(Rect.X + Rect.Width, Rect.Y + Rect.Height / 3);
            //Line2
            pArray[2] = new PointF(Rect.X, Rect.Y + Rect.Height / 3);
            pArray[3] = new PointF(Rect.X + Rect.Width, Rect.Y);
            //Line3
            pArray[4] = new PointF(Rect.X + Rect.Width / 2, Rect.Y + (Rect.Height / 3) / 2);
            pArray[5] = new PointF(Rect.X + Rect.Width / 2, Rect.Y + Rect.Height);

            // Set world transform of graphics object to translate.
            Matrix myMatrix = new Matrix();
            myMatrix.RotateAt((float)objGPSData.Angle, CenterPoint);
            myMatrix.TransformPoints(pArray);
            g.DrawLine(pen, pArray[0], pArray[1]);
            g.DrawLine(pen, pArray[2], pArray[3]);
            g.DrawLine(pen, pArray[4], pArray[5]);

            pen.Dispose();
        }

        /// <summary>
        /// Vẽ ký hiệu hình mũi tên
        /// </summary>
        /// <param name="g"></param>
        /// <param name="color"></param>
        protected void DrawArrow1(Graphics g, Color color)
        {
            InitRect();

            PointF[] pArray = new PointF[4];
            pArray[0] = new PointF(Rect.X + Rect.Width / 2, Rect.Y);
            pArray[1] = new PointF(Rect.X, Rect.Y + Rect.Height);
            pArray[2] = new PointF(Rect.X + Rect.Width / 2, Rect.Y + Rect.Height * ((float)2 / 3));
            pArray[3] = new PointF(Rect.X + Rect.Width, Rect.Y + Rect.Height);

            // Set world transform of graphics object to translate.
            Matrix myMatrix = new Matrix();
            myMatrix.RotateAt((float)objGPSData.Angle, CenterPoint);
            myMatrix.TransformPoints(pArray);

            SolidBrush brush = new SolidBrush(color);
            g.FillPolygon(brush, pArray);

            brush.Dispose();
        }

        /// <summary>
        /// Vẽ ký hiệu hình mũi tên 2
        /// </summary>
        /// <param name="g"></param>
        /// <param name="color"></param>
        protected void DrawArrow2(Graphics g, Color color)
        {
            InitRect();

            PointF[] pArray1 = new PointF[4];
            pArray1[0] = new PointF(Rect.X + Rect.Width * (4f / 10), Rect.Y);
            pArray1[1] = new PointF(Rect.X + Rect.Width * (6f / 10), Rect.Y);
            pArray1[2] = new PointF(Rect.X + Rect.Width * (6f / 10), Rect.Y + Rect.Height * (3f / 4));
            pArray1[3] = new PointF(Rect.X + Rect.Width * (4f / 10), Rect.Y + Rect.Height * (3f / 4));

            PointF[] pArray2 = new PointF[4];
            pArray2[0] = new PointF(Rect.X, Rect.Y + Rect.Height * (3f / 4));
            pArray2[1] = new PointF(Rect.X + Rect.Width / 2f, Rect.Y + Rect.Height / 2f);
            pArray2[2] = new PointF(Rect.X + Rect.Width, Rect.Y + Rect.Height * (3f / 4));
            pArray2[3] = new PointF(Rect.X + Rect.Width / 2f, Rect.Y + Rect.Height);

            // Set world transform of graphics object to translate.
            Matrix myMatrix = new Matrix();
            myMatrix.RotateAt((float)objGPSData.Angle, CenterPoint);

            myMatrix.TransformPoints(pArray1);
            //Pen pen = new Pen(color, 1);
            SolidBrush brush = new SolidBrush(color);
            g.FillPolygon(brush, pArray1);
            myMatrix.TransformPoints(pArray2);
            g.FillPolygon(brush, pArray2);

            //pen.Dispose();
            brush.Dispose();
        }

        /// <summary>
        /// Vẽ ký hiệu hình mũi tên 
        /// </summary>
        /// <param name="g"></param>
        /// <param name="color"></param>
        protected void DrawArrow(Graphics g, Color color)
        {
            InitRect();

            PointF[] pArray = new PointF[7];
            //pArray[0] = new PointF(Rect.X + (1f / 2) * Rect.Width, Rect.Y);
            //pArray[1] = new PointF(Rect.X, Rect.Y + (1f / 2) * Rect.Height);
            //pArray[2] = new PointF(Rect.X + (4f / 9) * Rect.Width, Rect.Y + (4f / 10) * Rect.Height);
            //pArray[3] = new PointF(Rect.X + (4f / 9) * Rect.Width, Rect.Y + Rect.Height);
            //pArray[4] = new PointF(Rect.X + (5f / 9) * Rect.Width, Rect.Y + Rect.Height);
            //pArray[5] = new PointF(Rect.X + (5f / 9) * Rect.Width, Rect.Y + (4f / 10) * Rect.Height);
            //pArray[6] = new PointF(Rect.X + Rect.Width, Rect.Y + (1f / 2) * Rect.Height);

            //pArray[0] = new PointF(Rect.X + (1f / 2) * Rect.Width, Rect.Y);
            //pArray[1] = new PointF(Rect.X, Rect.Y + (1f / 2) * Rect.Height);
            //pArray[2] = new PointF(Rect.X + (3f / 8) * Rect.Width, Rect.Y + (4f / 10) * Rect.Height);
            //pArray[3] = new PointF(Rect.X + (3f / 8) * Rect.Width, Rect.Y + Rect.Height);
            //pArray[4] = new PointF(Rect.X + (5f / 8) * Rect.Width, Rect.Y + Rect.Height);
            //pArray[5] = new PointF(Rect.X + (5f / 8) * Rect.Width, Rect.Y + (4f / 10) * Rect.Height);
            //pArray[6] = new PointF(Rect.X + Rect.Width, Rect.Y + (1f / 2) * Rect.Height);

            pArray[0] = new PointF(Rect.X + (1f / 2) * Rect.Width, Rect.Y);
            pArray[1] = new PointF(Rect.X, Rect.Y + (6f / 15) * Rect.Height);
            pArray[2] = new PointF(Rect.X + (3f / 8) * Rect.Width, Rect.Y + (5f / 15) * Rect.Height);
            pArray[3] = new PointF(Rect.X + (3f / 8) * Rect.Width, Rect.Y + Rect.Height);
            pArray[4] = new PointF(Rect.X + (5f / 8) * Rect.Width, Rect.Y + Rect.Height);
            pArray[5] = new PointF(Rect.X + (5f / 8) * Rect.Width, Rect.Y + (5f / 15) * Rect.Height);
            pArray[6] = new PointF(Rect.X + Rect.Width, Rect.Y + (6f / 15) * Rect.Height);

            // Set world transform of graphics object to translate.
            Matrix myMatrix = new Matrix();
            myMatrix.RotateAt((float)objGPSData.Angle, CenterPoint);
            myMatrix.TransformPoints(pArray);

            SolidBrush brush = new SolidBrush(color);
            g.FillPolygon(brush, pArray);
            //g.DrawRectangle(new Pen(Color.Orange), _rect.X, _rect.Y, _rect.Width, _rect.Height);
            brush.Dispose();
        }

        /// <summary>
        /// Vẽ ký hiệu hình ảnh máy báy
        /// </summary>
        /// <param name="g"></param>
        protected void DrawPlaneImage(Graphics g)
        {
            DrawImage(g, Resources.plane);
        }

        #endregion

        #endregion

        #region Abstract Functions

        protected abstract void InitRect();

        /// <summary>
        /// Vẽ các tọa độ gps
        /// </summary>
        /// <param name="g"></param>
        public abstract void Draw(Graphics g);

        /// <summary>
        /// Vẽ các tọa độ gps
        /// </summary>
        /// <param name="g"></param>
        public abstract void Draw(Graphics g, Color color);

        /// <summary>
        /// Vẽ các tọa độ gps có border bao quanh
        /// </summary>
        /// <param name="g"></param>
        public abstract void Draw(Graphics g, Color colorFill, Color colorBorder);

        /// <summary>
        /// Vẽ kèm theo label
        /// </summary>
        /// <param name="g"></param>
        /// <param name="label"></param>
        public abstract void Draw(Graphics g, string label);
        #endregion

        #region COM Processing
        
        ///// <summary>
        ///// Convert instance to byte array
        ///// </summary>
        ///// <returns></returns>
        //public char[] ConverToByteArray()
        //{
        //    char[] data = new char[GeneralClass.DATA_LEN];
        //    char[] comma = new char[1] { ',' };
        //    char[] hemisphereOUmeridienLat;
        //    int index = 0;

        //    //name: 4 byte
        //    char[] nameArray = Name.ToCharArray(0, GeneralClass.NAME_LEN);
        //    nameArray.CopyTo(data, index);
        //    index += nameArray.Length;
        //    //Add comma
        //    comma.CopyTo(data, index);

        //    //lat: 8 byte
        //    index++;
        //    char[] latArray;
        //    double value = LatGPS;
        //    if (value > 0)
        //        hemisphereOUmeridienLat = new char[1] { 'N' };
        //    else
        //        hemisphereOUmeridienLat = new char[1] { 'S' };
        //    latArray = string.Format("{0}{1}", new string(hemisphereOUmeridienLat), Math.Abs(value).ToString("00.00000").Replace(".", "")).ToCharArray(0, GeneralClass.LAT_LEN);
        //    latArray.CopyTo(data, index);
        //    index += latArray.Length;
        //    //Add comma
        //    comma.CopyTo(data, index);

        //    //lon: 9 byte
        //    index++;
        //    char[] lonArray;
        //    value = LonGPS;
        //    if (LonGPS > 0)
        //        hemisphereOUmeridienLat = new char[1] { 'E' };
        //    else
        //        hemisphereOUmeridienLat = new char[1] { 'W' };
        //    lonArray = string.Format("{0}{1}", new string(hemisphereOUmeridienLat), Math.Abs(value).ToString("000.00000").Replace(".", "")).ToCharArray(0, GeneralClass.LON_LEN);
        //    lonArray.CopyTo(data, index);
        //    index += lonArray.Length;
        //    //Add comma
        //    comma.CopyTo(data, index);

        //    //alt: 4 byte
        //    index++;
        //    char[] altArray;
        //    value = Alt;
        //    if (value < 10)
        //        altArray = string.Format("000{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
        //    else if (value < 100)
        //        altArray = string.Format("00{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
        //    else if (value < 1000)
        //        altArray = string.Format("0{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
        //    else if (value < 10000)
        //        altArray = string.Format("{0}", value.ToString()).ToCharArray(0, GeneralClass.ALT_LEN);
        //    else //error
        //        altArray = string.Format("-111").ToCharArray();
        //    altArray.CopyTo(data, index);
        //    index += altArray.Length;
        //    //Add comma
        //    comma.CopyTo(data, index);

        //    //speed: 3 byte
        //    index++;
        //    char[] speedArray;
        //    value = Speed;
        //    if (value < 10)
        //        speedArray = string.Format("00{0}", value.ToString()).ToCharArray(0, GeneralClass.SPEED_LEN);
        //    else if (value < 100)
        //        speedArray = string.Format("0{0}", value.ToString()).ToCharArray(0, GeneralClass.SPEED_LEN);
        //    else if (value < 1000)
        //        speedArray = string.Format("{0}", value.ToString()).ToCharArray(0, GeneralClass.SPEED_LEN);
        //    else //error
        //        speedArray = string.Format("-11").ToCharArray();
        //    speedArray.CopyTo(data, index);
        //    index += speedArray.Length;
        //    //Add comma
        //    comma.CopyTo(data, index);

        //    //angle: 3 byte
        //    index++;
        //    char[] angleArray;
        //    value = Angle;
        //    if (value < 10)
        //        angleArray = string.Format("00{0}", value.ToString()).ToCharArray(0, GeneralClass.ANGLE_LEN);
        //    else if (value < 100)
        //        angleArray = string.Format("0{0}", value.ToString()).ToCharArray(0, GeneralClass.ANGLE_LEN);
        //    else if (value < 1000)
        //        angleArray = string.Format("{0}", value.ToString()).ToCharArray(0, GeneralClass.ANGLE_LEN);
        //    else //error
        //        angleArray = string.Format("-11").ToCharArray();
        //    angleArray.CopyTo(data, index);

        //    return data;
        //}

        ///// <summary>
        ///// Lấy dữ liệu nhận từ cổng COM chuyển thành đối tượng
        ///// </summary>
        ///// <param name="data"></param>
        //public void UpdateFromCOMPort(string data)
        //{
        //    string[] valueArray = data.Split(new char[1] { ',' });
        //    LocalTime = DateTime.Now;
        //    Name = valueArray[0];
        //    string sign;

        //    sign = valueArray[1].Substring(0, 1);
        //    double degree = double.Parse(valueArray[1].Substring(1,2));
        //    double minute = double.Parse(valueArray[1].Substring(3));
        //    double lat = (minute/60000f) + degree;
        //    LatGPS = GeneralClass.StringCoordToDecimalCoord(sign + lat.ToString("#.00000"));

        //    sign = valueArray[2].Substring(0, 1);
        //    degree = double.Parse(valueArray[2].Substring(1, 3));
        //    minute = double.Parse(valueArray[2].Substring(4));
        //    double lon = (minute / 60000f) + degree;
        //    LonGPS = GeneralClass.StringCoordToDecimalCoord(sign + lon.ToString("#.00000"));

        //    Alt = Double.Parse(valueArray[3]);
        //    Speed = Double.Parse(valueArray[4]);
        //    Angle = Double.Parse(valueArray[5]);

        //    //InitRect();
        //}
        #endregion

        #region HitTest
        
        /// <summary>
        /// Hit test.
        /// Return value: -1 - no hit
        ///                0 - hit anywhere
        ///                > 1 - handle number
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public int HitTest(PointF point)
        {
            if (Selected)
            {
                for (int i = 1; i <= HandleCount; i++)
                {
                    if (GetHandleRectangle(i).Contains(point))
                        return i;
                }
            }

            if (PointInObject(point))
                return 0;

            return -1;
        }

        /// <summary>
        /// Get handle rectangle by 1-based number
        /// </summary>
        /// <param name="handleNumber"></param>
        /// <returns></returns>
        protected RectangleF GetHandleRectangle(int handleNumber)
        {
            PointF point = GetHandle(handleNumber);

            return new RectangleF(point.X - 3, point.Y - 3, 7, 7);
        }

        /// <summary>
        /// Get handle point by 1-based number
        /// </summary>
        /// <param name="handleNumber"></param>
        /// <returns></returns>
        protected PointF GetHandle(int handleNumber)
        {
            float x, y, xCenter, yCenter;

            xCenter = _rect.X + _rect.Width / 2;
            yCenter = _rect.Y + _rect.Height / 2;
            x = _rect.X;
            y = _rect.Y;

            switch (handleNumber)
            {
                case 1:
                    x = _rect.X;
                    y = _rect.Y;
                    break;
                case 2:
                    x = xCenter;
                    y = _rect.Y;
                    break;
                case 3:
                    x = _rect.Right;
                    y = _rect.Y;
                    break;
                case 4:
                    x = _rect.Right;
                    y = yCenter;
                    break;
                case 5:
                    x = _rect.Right;
                    y = _rect.Bottom;
                    break;
                case 6:
                    x = xCenter;
                    y = _rect.Bottom;
                    break;
                case 7:
                    x = _rect.X;
                    y = _rect.Bottom;
                    break;
                case 8:
                    x = _rect.X;
                    y = yCenter;
                    break;
            }

            return new PointF(x, y);

        }

        protected bool PointInObject(PointF point)
        {
            return _rect.Contains(point);
        }

        #endregion

        #region Compare Object
        
        public override bool Equals(System.Object obj)
        {
            // If parameter is null return false.
            if (obj == null)
            {
                return false;
            }

            // If parameter cannot be cast to Point return false.
            BaseGPSPoint p = obj as BaseGPSPoint;
            if ((System.Object)p == null)
            {
                return false;
            }

            // Return true if the fields match:
            //return (objGPSData.LocalTime == p.objGPSData.LocalTime) && (objGPSData.LonGPS == p.objGPSData.LonGPS) && (objGPSData.LatGPS == p.objGPSData.LatGPS) && (objGPSData.Alt == p.objGPSData.Alt) && (objGPSData.Speed == p.objGPSData.Speed) && (objGPSData.Angle == p.objGPSData.Angle);
            return objGPSData.Equals(p.objGPSData);
        }

        public bool Equals(BaseGPSPoint p)
        {
            // If parameter is null return false:
            if ((object)p == null)
            {
                return false;
            }

            // Return true if the fields match:
            //return (objGPSData.LocalTime == p.objGPSData.LocalTime) && (objGPSData.LonGPS == p.objGPSData.LonGPS) && (objGPSData.LatGPS == p.objGPSData.LatGPS) && (objGPSData.Alt == p.objGPSData.Alt) && (objGPSData.Speed == p.objGPSData.Speed) && (objGPSData.Angle == p.objGPSData.Angle);
            return objGPSData.Equals(p.objGPSData);
        }

        public override int GetHashCode()
        {
            return (int)objGPSData.LonGPS ^ (int)objGPSData.LatGPS ^ (int)objGPSData.Alt ^ (int)objGPSData.Speed ^ (int)objGPSData.Angle ^ (int)objGPSData.Time.ToOADate() ^ (int)objGPSData.SatNum;
        }

        #endregion
    }

    }
